class Package(object):
    filename = None
    id = None
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

class Installer(object):
    """ Installers work with package files or ids; installing, updating or erasing them.
    
        Properties:
            root: The root directory to install/update/erase packages.
    """
    root = None
    
    def __init__(self, backend, root):
        self.backend = backend 
        self.root = root
        
    def install(self, package):
        """ Install a package file. 
        """
        self.backend.install(self, package)
    
    
    def update(self, package):
        """ Update an installed package with a file. 
        """
        self.backend.update(self, package)
    
    
    def erase(self, package):
        """ Erase an installed package. 
        """
        self.backend.erase(self, package)
    
    
    def run(self):
        self.backend.run(self)

import subprocess

class SubprocessBackend(object):
    """ This backend is used to overcome issues with rpm chrooting.
    """
    JOB_INSTALL = 1
    JOB_UPDATE = 2
    JOB_ERASE = 3
    
    def __init__(self):
        self.jobs = []
        
    def install(self, installer, package):
        self.jobs.append((self.JOB_INSTALL, package))
    
    def update(self, installer, package):
        self.jobs.append((self.JOB_UPDATE, package))
    
    def erase(self, installer, package):
        self.jobs.append((self.JOB_ERASE, package))
    
    
    def run(self, installer):
        root = installer.root
        
        for job in self.jobs:
            type, package = job
            
            cmd = ['rpm', '--dbpath', '%s/var/lib/rpm/' % root]
            
            if type == self.JOB_INSTALL or type == self.JOB_UPDATE:
                cmd.append('--relocate')
                cmd.append('/=%s' % root)
                cmd.append('--nodeps')
                cmd.append('-Uvh')
                cmd.append(package.filename)
                print subprocess.check_output(cmd)
                
            elif type == self.JOB_ERASE:
                cmd.append('--erase')
                cmd.append(package.id)
                print subprocess.check_output(cmd)
        
     
import rpm
import os
import logging
logger = logging.getLogger('pkgr.rpm')
class ApiBackend(object):
    JOB_INSTALL = 1
    JOB_UPDATE = 2
    JOB_ERASE = 3
    def __init__(self):
        self.jobs = []
        self.package_map = {}
        
    def install(self, installer, package):
        self.package_map[package.id] = package
        self.jobs.append((self.JOB_INSTALL, package))
    
    def update(self, installer, package):
        self.package_map[package.id] = package
        self.jobs.append((self.JOB_UPDATE, package))
    
    def erase(self, installer, package):
        self.package_map[package.id] = package
        self.jobs.append((self.JOB_ERASE, package))
    
    def _rpm_run_callback(self, reason, amount, total, package_header, d):
        package = None
        header = None
        if package_header and len(package_header) == 2:
            package,header = package_header
            
        if reason == rpm.RPMCALLBACK_INST_OPEN_FILE:
            logger.log(5, 'Open file %s %s %s %s', amount, total, package, d)
            self.last_open = file(self.package_map[package.id].filename)
            return self.last_open.fileno()
        elif reason == rpm.RPMCALLBACK_INST_CLOSE_FILE:
            logger.log(5, 'Close file %s %s %s %s', amount, total, package, d)
            #return self.newpkgsfp[package.id].close()
            self.last_open.close()
        elif reason == rpm.RPMCALLBACK_INST_START:
            logger.log(5, 'Install start %s %s %s %s', amount, total, package, d)
        elif reason == rpm.RPMCALLBACK_UNINST_START:
            pass
            # argh, p is just the name of the package
            #if package in self.erasenamehelper:
            #    package = self.erasenamehelper[package]
            #    logger.log(5, 'Erase start %s %s %s %s', amount, total, package, d)
        elif reason == rpm.RPMCALLBACK_CPIO_ERROR:
            logger.error('CPIO_ERROR ERROR %s %s %s %s', amount, total, package, d)
        elif reason == rpm.RPMCALLBACK_UNPACK_ERROR:
            logger.error('UNPACK_ERROR ERROR %s %s %s %s', amount, total, package, d)
        elif reason == rpm.RPMCALLBACK_SCRIPT_ERROR:
            logger.error('SCRIPT_ERROR ERROR %s %s %s %s', amount, total, package, d)
    
    def run(self, installer):
        #rpmverbosity = 'debug'
        # set up the transaction to record output from scriptlets
        #self._script_output_file = tempfile.NamedTemporaryFile()
        #self._script_output_fd = open(self._script_output_file.name, 'w+b')
        #logging.debug('Logging rpm to %s', self._script_output_file.name)
        #rpm.setScriptFd(self._script_output_fd)
        #rpm.setLogFile(self._script_output_fd)
        #if rpmverbosity not in RPMLOG_LEVELS:
        #    raise ValueError('rpmverbosity not one of %s' % RPMLOG_LEVELS.keys())
        #rpm.setVerbosity(RPMLOG_LEVELS.get(rpmverbosity))
        
        
        # START INSTALL
        
        root = installer.root
        
        
        ts = rpm.TransactionSet(root)
        ts.initDB()
        #ts.rebuildDB()~(rpm.RPMVSF_NORSA|rpm.RPMVSF_NODSA)
        ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
        #ts.setFlags(rpm.RPMTRANS_FLAG_NOSCRIPTS|rpm.RPMTRANS_FLAG_NOTRIGGERS)
        #ts.setProbFilter(rpm.RPMPROB_FILTER_FORCERELOCATE)
        
        
        for job in self.jobs:
            type, package = job
            if type == self.JOB_INSTALL or type == self.JOB_UPDATE:
                rpmfile = package.filename
                f = open(rpmfile)
                h = ts.hdrFromFdno(f.fileno())
                os.lseek(f.fileno(), 0, os.SEEK_SET)
                ts.addInstall(h, (package, h), 'u')
            elif type == self.JOB_ERASE:
                ts.addErase(package.id)
                
        checkproblems = ts.check()
        if checkproblems:
            print checkproblems
            raise Exception("Error with rpm transaction check: %s" % str(checkproblems))
            
        ts.check()
        if ts.problems():
            raise Exception('RPM Check had the following problems: %s' % str(ts.problems()))
        # No problems, order the transaction and let rpm free memory
        # which is not needed from this point onwards.
        ts.order()
        ts.clean()
        runproblems = ts.run(self._rpm_run_callback, None)     